package com.haixin.security.config;

import com.haixin.security.handler.MyAuthenticationFailureHandler;
import com.haixin.security.handler.MyAuthenticationSuccessHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

// Security配置类
@Configuration
@EnableWebSecurity
@Slf4j
public class SecurityConfig {

    @Autowired
    DataSource dataSource;


    /**
     * 创建Security过滤器链对象
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity security) throws Exception {
//    创建匿名内部类，实现认证相关配置
//        在security框架中，登录页面地址和登录请求地址，默认都是不校验权限的。不校验认证的。
//        建议把登录页面和登录请求地址，放在最前面，这样，在访问其他页面时，就先校验登录权限，再校验其他权限。
        Customizer<FormLoginConfigurer<HttpSecurity>> customizer = new Customizer<FormLoginConfigurer<HttpSecurity>>() {
            @Override
            public void customize(FormLoginConfigurer<HttpSecurity> configurer) {
//            具体的认证配置
                configurer.loginPage("/login")//设置登录页面访问地址，默认是/login。必须是get请求。
                        .loginProcessingUrl("/userLogin")//设置登录请求提交地址，默认是/login，必须是post请求
                        .usernameParameter("name")//设置请求参数名称，用户名 默认是username
                        .passwordParameter("word")//设置请求参数名称，密码 默认是password
//        security框架中，登录认证成功后默认重定向到最后一次访问的页面地址。如果上次访问的是login登录页面，那么就会触触发下面的重定向或者转发到指定的页面
//                        这些配置同时配置时，优先使用转发地址。
                        .defaultSuccessUrl("/index")//设置成功后的重定向地址
                        .successForwardUrl("/index")//设置成功后的转发地址
                        .successHandler(new MyAuthenticationSuccessHandler("/index",true))
                        .failureForwardUrl("/errorLogin")//设置失败后的转发地址
                        .failureUrl("/errorLogin")//设置失败后的重定向地址
                        .failureHandler(new MyAuthenticationFailureHandler("/errorLogin",true))
                ;
            }
        };
//        设置认证配置
        security.formLogin(customizer);

//        配置rememberMe相关
        Customizer<RememberMeConfigurer<HttpSecurity>> rememberMe = new Customizer<RememberMeConfigurer<HttpSecurity>>() {

            @Override
            public void customize(RememberMeConfigurer<HttpSecurity> configurer) {
                configurer.
                        tokenRepository(persistentTokenRepository())//设置保存记住我的具体对象
                        .rememberMeParameter("remember-me")
                ;

            }
        };
        security.rememberMe(rememberMe);

//        授权配置
        security.authorizeRequests().antMatchers("/login","/userLogin","/errorLogin").permitAll()//登录页面可以不用认证
                .anyRequest().authenticated();//访问其他页面都需要认证
//关闭csrf防护
        security.csrf().disable();
        return security.build();
    }


    /**
     * 创建一个bean对象
     * 这个bean对象直接使用security提供的实现类即可
     * jdbcTokenRepository 基于DataSource数据库连接池，访问指定数据库
     * 把认证成功的，需要rememberMe的用户数据保存到数据库中
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();

        jdbcTokenRepository.setDataSource(dataSource);
//        初始化参数，会创建数据库表接口，仅第一次启动项目的时候设置为true。后续再次启动修改会false
//        如果设置为true，启动时，自动连接数据库创建表格。表格是保存rememberMe的用户信息的。
        jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }


    /**
     * 使用BCryptPasswordEncoder
     */
    @Bean
    public PasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();
    }


//    // 定义认证逻辑
//    @Bean
//    public UserDetailsService userDetailsService(){
//        // 1.使用内存数据进行认证
//        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//        // 2.创建两个用户
//        UserDetails user1 = User.withUsername("baizhan").password("123").authorities("admin").build();
//        UserDetails user2 = User.withUsername("sxt").password("456").authorities("admin").build();
//        // 3.将这两个用户添加到内存中
//        manager.createUser(user1);
//        manager.createUser(user2);
//        return manager;
//    }


    /**
     *  自定义密码编码器，不解析密码
     *  */
//    @Bean
//    public PasswordEncoder passwordEncoder()
//    {
//        return new MyPasswordEncoder();
//    }

}